/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */#include"DocumentTimeline.h"#include"mozilla/dom/DocumentTimelineBinding.h"#include"AnimationUtils.h"#include"nsContentUtils.h"#include"nsDOMMutationObserver.h"#include"nsDOMNavigationTiming.h"#include"nsIPresShell.h"#include"nsPresContext.h"#include"nsRefreshDriver.h"namespacemozilla{namespacedom{NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentTimeline)NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocumentTimeline,AnimationTimeline)tmp->UnregisterFromRefreshDriver();if(tmp->isInList()){tmp->remove();}NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocumentTimeline,AnimationTimeline)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDNS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DocumentTimeline,AnimationTimeline)NS_IMPL_CYCLE_COLLECTION_TRACE_ENDNS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocumentTimeline)NS_INTERFACE_MAP_END_INHERITING(AnimationTimeline)NS_IMPL_ADDREF_INHERITED(DocumentTimeline,AnimationTimeline)NS_IMPL_RELEASE_INHERITED(DocumentTimeline,AnimationTimeline)JSObject*DocumentTimeline::WrapObject(JSContext*aCx,JS::Handle<JSObject*>aGivenProto){returnDocumentTimelineBinding::Wrap(aCx,this,aGivenProto);}/* static */already_AddRefed<DocumentTimeline>DocumentTimeline::Constructor(constGlobalObject&aGlobal,constDocumentTimelineOptions&aOptions,ErrorResult&aRv){nsIDocument*doc=AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());if(!doc){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}TimeDurationoriginTime=TimeDuration::FromMilliseconds(aOptions.mOriginTime);if(originTime==TimeDuration::Forever()||originTime==-TimeDuration::Forever()){aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>(NS_LITERAL_STRING("Origin time"));returnnullptr;}RefPtr<DocumentTimeline>timeline=newDocumentTimeline(doc,originTime);returntimeline.forget();}Nullable<TimeDuration>DocumentTimeline::GetCurrentTime()const{returnToTimelineTime(GetCurrentTimeStamp());}TimeStampDocumentTimeline::GetCurrentTimeStamp()const{nsRefreshDriver*refreshDriver=GetRefreshDriver();TimeStamprefreshTime=refreshDriver?refreshDriver->MostRecentRefresh():TimeStamp();// Always return the same object to benefit from return-value optimization.TimeStampresult=!refreshTime.IsNull()?refreshTime:mLastRefreshDriverTime;// If we don't have a refresh driver and we've never had one use the// timeline's zero time.if(result.IsNull()){nsDOMNavigationTiming*timing=mDocument->GetNavigationTiming();if(timing){result=timing->GetNavigationStartTimeStamp();// Also, let this time represent the current refresh time. This way// we'll save it as the last refresh time and skip looking up// navigation timing each time.refreshTime=result;}}if(!refreshTime.IsNull()){mLastRefreshDriverTime=refreshTime;}returnresult;}Nullable<TimeDuration>DocumentTimeline::ToTimelineTime(constTimeStamp&aTimeStamp)const{Nullable<TimeDuration>result;// Initializes to nullif(aTimeStamp.IsNull()){returnresult;}nsDOMNavigationTiming*timing=mDocument->GetNavigationTiming();if(MOZ_UNLIKELY(!timing)){returnresult;}result.SetValue(aTimeStamp-timing->GetNavigationStartTimeStamp()-mOriginTime);returnresult;}voidDocumentTimeline::NotifyAnimationUpdated(Animation&aAnimation){AnimationTimeline::NotifyAnimationUpdated(aAnimation);if(!mIsObservingRefreshDriver){nsRefreshDriver*refreshDriver=GetRefreshDriver();if(refreshDriver){MOZ_ASSERT(isInList(),"We should not register with the refresh driver if we are not"" in the document's list of timelines");refreshDriver->AddRefreshObserver(this,FlushType::Style);mIsObservingRefreshDriver=true;}}}voidDocumentTimeline::WillRefresh(mozilla::TimeStampaTime){MOZ_ASSERT(mIsObservingRefreshDriver);MOZ_ASSERT(GetRefreshDriver(),"Should be able to reach refresh driver from within WillRefresh");boolneedsTicks=false;nsTArray<Animation*>animationsToRemove(mAnimations.Count());nsAutoAnimationMutationBatchmb(mDocument);for(Animation*animation=mAnimationOrder.getFirst();animation;animation=animation->getNext()){// Skip any animations that are longer need associated with this timeline.if(animation->GetTimeline()!=this){// If animation has some other timeline, it better not be also in the// animation list of this timeline object!MOZ_ASSERT(!animation->GetTimeline());animationsToRemove.AppendElement(animation);continue;}needsTicks|=animation->NeedsTicks();// Even if |animation| doesn't need future ticks, we should still// Tick it this time around since it might just need a one-off tick in// order to dispatch events.animation->Tick();if(!animation->IsRelevant()&&!animation->NeedsTicks()){animationsToRemove.AppendElement(animation);}}for(Animation*animation:animationsToRemove){RemoveAnimation(animation);}if(!needsTicks){// We already assert that GetRefreshDriver() is non-null at the beginning// of this function but we check it again here to be sure that ticking// animations does not have any side effects that cause us to lose the// connection with the refresh driver, such as triggering the destruction// of mDocument's PresShell.MOZ_ASSERT(GetRefreshDriver(),"Refresh driver should still be valid at end of WillRefresh");UnregisterFromRefreshDriver();}}voidDocumentTimeline::NotifyRefreshDriverCreated(nsRefreshDriver*aDriver){MOZ_ASSERT(!mIsObservingRefreshDriver,"Timeline should not be observing the refresh driver before"" it is created");if(!mAnimationOrder.isEmpty()){MOZ_ASSERT(isInList(),"We should not register with the refresh driver if we are not"" in the document's list of timelines");aDriver->AddRefreshObserver(this,FlushType::Style);mIsObservingRefreshDriver=true;}}voidDocumentTimeline::NotifyRefreshDriverDestroying(nsRefreshDriver*aDriver){if(!mIsObservingRefreshDriver){return;}aDriver->RemoveRefreshObserver(this,FlushType::Style);mIsObservingRefreshDriver=false;}voidDocumentTimeline::RemoveAnimation(Animation*aAnimation){AnimationTimeline::RemoveAnimation(aAnimation);if(mIsObservingRefreshDriver&&mAnimations.IsEmpty()){UnregisterFromRefreshDriver();}}TimeStampDocumentTimeline::ToTimeStamp(constTimeDuration&aTimeDuration)const{TimeStampresult;RefPtr<nsDOMNavigationTiming>timing=mDocument->GetNavigationTiming();if(MOZ_UNLIKELY(!timing)){returnresult;}result=timing->GetNavigationStartTimeStamp()+(aTimeDuration+mOriginTime);returnresult;}nsRefreshDriver*DocumentTimeline::GetRefreshDriver()const{nsIPresShell*presShell=mDocument->GetShell();if(MOZ_UNLIKELY(!presShell)){returnnullptr;}nsPresContext*presContext=presShell->GetPresContext();if(MOZ_UNLIKELY(!presContext)){returnnullptr;}returnpresContext->RefreshDriver();}voidDocumentTimeline::UnregisterFromRefreshDriver(){if(!mIsObservingRefreshDriver){return;}nsRefreshDriver*refreshDriver=GetRefreshDriver();if(!refreshDriver){return;}refreshDriver->RemoveRefreshObserver(this,FlushType::Style);mIsObservingRefreshDriver=false;}}// namespace dom}// namespace mozilla